/*
 * Copyright (c) 2012, Marcin Czekaj
 * All rights reserved.
 *
 * This software is open-source under the BSD license; see LICENSE.TXT for a description.
 */
package pl.edu.uj.fais.graph;

import java.awt.Color;
import java.awt.Paint;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.geom.Ellipse2D;

import org.apache.commons.collections15.Transformer;
import org.apache.commons.collections15.functors.ConstantTransformer;

import pl.edu.uj.fais.controller.Controller;
import pl.edu.uj.fais.graph.elements.Link;
import pl.edu.uj.fais.graph.elements.Node;
import pl.edu.uj.fais.gui.GraphCanvas;
import pl.edu.uj.fais.mode.GraphMode;
import edu.uci.ics.jung.visualization.RenderContext;
import edu.uci.ics.jung.visualization.decorators.EdgeShape;
import edu.uci.ics.jung.visualization.decorators.PickableEdgePaintTransformer;
import edu.uci.ics.jung.visualization.decorators.PickableVertexPaintTransformer;
import edu.uci.ics.jung.visualization.picking.PickedState;
import edu.uci.ics.jung.visualization.renderers.DefaultVertexLabelRenderer;
import edu.uci.ics.jung.visualization.renderers.Renderer.VertexLabel.Position;

public class GraphStyle {
    
    public static final Color BACKGROUND = Color.LIGHT_GRAY;
    public static final Color SELECTION = Color.YELLOW;
    
    public static final Color NODE_LABEL = Color.BLUE;
    public static final Color NODE_LABEL_SELECTED = SELECTION;
    
    public static final Color NODE_STROKE = Color.DARK_GRAY;
    public static final Color NODE_STROKE_PROXY = Color.GRAY;
    
    public static final Color NODE_FILL = Color.BLUE;
    public static final Color NODE_FILL_PROXY = Color.GRAY;
    public static final Color NODE_FILL_SELECTED = SELECTION;
    
    public static final Color LINK_STROKE = Color.GRAY;
    public static final Color LINK_STROKE_SELECTED = SELECTION;
    
    public static final float ZOOM_IN_FACTOR = 1.1f;
    public static final float ZOOM_OUT_FACTOR = 0.9f;
    
    private static boolean showLabels = true;
    
    private Controller controller = Controller.getInstance();
    private GraphCanvas canvas;
    
    private RenderContext<Node, Link> context;
    private PickedState<Node> selectedNodes;
    private PickedState<Link> selectedLinks;
    
    public static void apply(GraphCanvas canvas) {
        GraphStyle style = new GraphStyle(canvas);
        style.apply();
    }
    
    public static void toggleLabels(GraphCanvas canvas) {
        showLabels = !showLabels;
        setLabels(canvas);
    }
    
    private static void setLabels(GraphCanvas canvas) {
        
        RenderContext<Node, Link> context = canvas.getRenderContext();
        
        if (showLabels) {
            context.setVertexLabelTransformer(new Transformer<Node, String>() {
                
                @Override
                public String transform(Node node) {
                    
                    GraphMode mode = Controller.getInstance().getGraphMode();
                    
                    // hide labels of proxy nodes
                    if (mode == GraphMode.HYPERGRAPH) {
                        if (node.isProxyNode()) {
                            return null;
                        }
                    }
                    
                    return node.toString();
                }
            });
        } else {
            context.setVertexLabelTransformer(new Transformer<Node, String>() {
                
                @Override
                public String transform(Node node) {
                    return null;
                }
            });
        }
    }
    private GraphStyle(GraphCanvas canvas) {
        
        this.canvas = canvas;
        
        context = canvas.getRenderContext();
        selectedNodes = canvas.getPickedVertexState();
        selectedLinks = canvas.getPickedEdgeState();
    }
    
    private void apply() {
        setCanvasColors();
        setNodeLabels();
        setNodes();
        setLinks();
    }
    
    private void setCanvasColors() {
        canvas.setBackground(BACKGROUND);
        canvas.setForeground(NODE_LABEL);
    }
    
    private void setNodeLabels() {
        // set font
    	context.setVertexFontTransformer(new ConstantTransformer(null)); // Workaround for long FontMetrics loading for Helvetica
        
        // set color and position
        context.setVertexLabelRenderer(new DefaultVertexLabelRenderer(NODE_LABEL_SELECTED));
        canvas.getRenderer().getVertexLabelRenderer().setPosition(Position.S);
        
        setLabels(canvas);
    }
    
    private void setNodes() {
        
        // node shape
        context.setVertexShapeTransformer(new Transformer<Node, Shape>() {
            
            @Override
            public Shape transform(Node node) {
                
                GraphMode mode = controller.getGraphMode();
                
                if (mode == GraphMode.HYPERGRAPH) {
                    if (node.isProxyNode()) {
                        return new Rectangle(-5, -5, 10, 10);
                    }
                }
                
                return new Ellipse2D.Double(-10, -10, 20, 20);
            }
        });
        
        // node stroke color
        context.setVertexDrawPaintTransformer(new Transformer<Node, Paint>() {
            
            @Override
            public Paint transform(Node node) {
                
                GraphMode mode = controller.getGraphMode();
                
                if (mode == GraphMode.HYPERGRAPH) {
                    if (node.isProxyNode()) {
                        return NODE_STROKE_PROXY;
                    }
                }
                
                return NODE_STROKE;
            }
        });
        
        // node fill color
        context.setVertexFillPaintTransformer(new PickableVertexPaintTransformer<Node>(selectedNodes,
                                                                                       NODE_FILL,
                                                                                       NODE_FILL_SELECTED) {
            
            @Override
            public Paint transform(Node node) {
                
                GraphMode mode = controller.getGraphMode();
                
                if (mode == GraphMode.HYPERGRAPH) {
                    if (node.isProxyNode()) {
                        if (pi.isPicked(node)) {
                            return picked_paint;
                        } else {
                            return NODE_FILL_PROXY;
                        }
                    }
                }
                
                if (pi.isPicked(node)) {
                    return picked_paint;
                } else {
                    return fill_paint;
                }
            }
        });
    }
    
    private void setLinks() {
        
        // draw links as straight lines
        context.setEdgeShapeTransformer(new EdgeShape.Line<Node, Link>());
        
        // link stroke color
        context.setEdgeDrawPaintTransformer(new PickableEdgePaintTransformer<Link>(selectedLinks,
                                                                                   LINK_STROKE,
                                                                                   LINK_STROKE_SELECTED));
    }
}
